﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Farigola.Engine.Reposit;
using System.Xml;
using System.IO;
using Ionic.Zip;
using Farigola.Engine.SystemData.Entity.inst;
using Farigola.Engine.Factories;
using Farigola.Engine.PersistXml.TestUnit.DataBuild;
using System.Threading;

namespace Farigola.Engine.PersistXml
{
    /// <summary>
    /// The concrete repository to persist data content of the project, in xml.
    /// 
    /// The zip file library is: DotNetZip Library
    /// coming from :http://dotnetzip.codeplex.com/
    /// </summary>
    public class ProjectRepositXml: IProjectReposit
    {
        /// <summary>
        /// The factory to build objects on load.
        /// </summary>
        private Factory _factory;

        /// <summary>
        /// Manager of tasks activity in the application (trace)
        /// Manage alos errors occurs in the engine.
        /// </summary>
        private AppActivityMgr _appActivityMgr;

        /// <summary>
        /// The current project
        /// </summary>
        //private Project _project;

        /// <summary>
        /// The file of the project zip, provided by the user.
        /// </summary>
        //private string _fileName = "";

        //=====================================================================
        #region Generals methods

        //---------------------------------------------------------------------
        /// <summary>
        /// provide the factory, need to build objects.
        /// </summary>
        /// <param name="factory"></param>
        public void SetFactory(Factory factory)
        {
            _factory = factory;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// provide the activity manager, to trace tasks and errors.
        /// </summary>
        /// <param name="appActivityMgr"></param>
        public void SetAppActivityMgr(AppActivityMgr appActivityMgr)
        {
            _appActivityMgr = appActivityMgr;
        }

        #endregion

        //=====================================================================
        #region XXX methods

        //---------------------------------------------------------------------
        /// <summary>
        /// check the fileName, for save.
        /// 
        /// Extract the path and check it.
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool CheckFileNameToSave(string fileName)
        {
            try{
                // extract the directory and check it
                string directory = Path.GetDirectoryName(fileName);
                if(!Directory.Exists(directory))
                    return false;

                return true;
            }catch{
                return false;
            }
        }

        #endregion

        //=====================================================================
        #region Save methods

        //---------------------------------------------------------------------
        /// <summary>
        /// Add a new entry to the zip file.
        /// </summary>
        /// <param name="zip"></param>
        /// <param name="name"></param>
        /// <param name="xmlDoc"></param>
        /// <returns></returns>
        private bool PutXmlDocToZip(ZipFile zip, string name, XmlDocument xmlDoc)
        {
            MemoryStream memStream = new MemoryStream();
            xmlDoc.Save(memStream);
            memStream.Position = 0;

            // better to export to a bytes array, after that, can close the stream
            ZipEntry e = zip.AddEntry(name, memStream.ToArray());
            memStream.Close();
            return true;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// now save the zip file.
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool SaveZipToFile(ZipFile zip, string fileName, out string errMsg)
        {
            errMsg="";
            try
            {
                zip.Save(fileName);
                return true;
            }
            catch(Exception e)
            {
                errMsg= e.Message;
                return false;
            } 
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// save generals parameters of the project,
        /// </summary>
        /// <param name="project"></param>
        /// <returns></returns>
        //private bool SaveProjectXml(string fileName, XmlDocument xmlDocPrjParams)
        //{
        //    MemoryStream xmlStream = new MemoryStream();
        //    xmlDocPrjParams.Save(xmlStream);
        //    xmlStream.Position = 0;

        //    //StreamReader sr = new StreamReader(
        //    using (ZipFile zip = new ZipFile())
        //    {
        //        ZipEntry e = zip.AddEntry("params.xml", xmlStream);
        //        zip.Save(fileName);
        //    }
            
        //    //sr.Close();
        //    xmlStream.Close();
        //    return true;
        //}

        //---------------------------------------------------------------------
        /// <summary>
        /// Save the project.
        /// </summary>
        /// <param name="project"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public bool SaveProject(Project project, string fileName)
        {
            bool res;

            if (_factory == null)
                return false;

            if (project == null)
                return false;

            // check the fileName
            //if (!CheckFileNameToSave(fileName))
            //    return _appActivityMgr.AddErrorRetFalse(ProjectRepositXmlDef.Error.FileNameWrong.ToString(), fileName);

            // erase existing previous file if present?
            // todo:
        
            // transform the project content (instances) to xml data
            TransformProjectToXml transformProjectToXml = new TransformProjectToXml(_factory);

            XmlDocument xmlDocPrjParams = transformProjectToXml.PutProjectParamsToXml(project);

            // put project types to an xml doc
            XmlDocument xmlDocPrjTypes;
            XmlDocument xmlDocPrjEntityTree;
            res = transformProjectToXml.PutProjectContentToXml(project, out xmlDocPrjTypes, out xmlDocPrjEntityTree);

            ZipFile zip = new ZipFile();

            res = PutXmlDocToZip(zip, ProjectRepositXmlDef.XmlDocParamsFileName,  xmlDocPrjParams);
            res = PutXmlDocToZip(zip, ProjectRepositXmlDef.XmlDocTypesFileName, xmlDocPrjTypes);
            res = PutXmlDocToZip(zip, ProjectRepositXmlDef.XmlDocEntityTreeFileName, xmlDocPrjEntityTree);

            // check all returns
            // todo:

            // now save the zip file
            string errMsg;
            if (!SaveZipToFile(zip, fileName, out errMsg))
                return _appActivityMgr.AddErrorRetFalse(ProjectRepositXmlDef.ErrorCode.UnableToSaveFile.ToString(), fileName, errMsg);


            return true;
        }
        #endregion

        //=====================================================================
        #region Load methods

        //---------------------------------------------------------------------
        /// <summary>
        /// check the fileName, for save.
        /// 
        /// Extract the path and check it.
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool CheckFileNameToLoad(string fileName)
        {
            try
            {
                if(!File.Exists(fileName))
                    return false;
                return true;
            }
            catch
            {
                return false;
            }
        }

        //---------------------------------------------------------------------
        public bool ReadZip(string fileName, out ZipFile zip)
        {
            zip = null;
            try
            {
                // check the file
                // todo:


                zip = new ZipFile();
                zip = ZipFile.Read(fileName);
                return true;
            }
            catch
            {
                return false;
            }

        }

        //---------------------------------------------------------------------
        private bool LoadXmlDocFromZipFile(ZipFile zip, string entryName, out XmlDocument xmlDoc)
        {
            xmlDoc = new XmlDocument();

            try
            {
                MemoryStream memStream = new MemoryStream();
                ZipEntry e = zip[entryName];
                if (e == null)
                    return false;

                e.Extract(memStream);
                memStream.Position = 0;
                xmlDoc.Load(memStream);

                memStream.Close();

                return true;
            }
            catch
            {
                return false;
            }
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Load the project.
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public bool LoadProject(string fileName, out Project project)
        {
            bool res;
            project = null;

            // check that the file exists
            if (!CheckFileNameToLoad(fileName))
                return _appActivityMgr.AddErrorRetFalse(ProjectRepositXmlDef.ErrorCode.FileNotExist.ToString(), fileName);

            // load and decode the content of the file (its a zip file, contains 3 files)
            ZipFile zip = new ZipFile();
            if(!ReadZip(fileName, out zip))
                return _appActivityMgr.AddErrorRetFalse(ProjectRepositXmlDef.ErrorCode.CantReadFile.ToString(), fileName);

            XmlDocument xmlDocPrjParams;
            XmlDocument xmlDocPrjTypes;
            XmlDocument xmlDocPrjEntityTree;

            // load the xml params content
            res = LoadXmlDocFromZipFile(zip, ProjectRepositXmlDef.XmlDocParamsFileName, out xmlDocPrjParams);

            // decode the params xml content file 
            TransformXmlToProject transformXmlToProject = new TransformXmlToProject(_factory);

            // load main data (parms) and create the project
            string listXmlNodeOnError;
            if (!transformXmlToProject.PutXmlParamsToProject(xmlDocPrjParams, out project, out listXmlNodeOnError))
                return _appActivityMgr.AddErrorRetFalse(ProjectRepositXmlDef.ErrorCode.ParamsWrong.ToString(), listXmlNodeOnError);
            
            // load the xml types content
            res = LoadXmlDocFromZipFile(zip, ProjectRepositXmlDef.XmlDocTypesFileName, out xmlDocPrjTypes);
            // todo:

            // decode types
            // todo:

            // load the xml entityTree content
            res = LoadXmlDocFromZipFile(zip, ProjectRepositXmlDef.XmlDocEntityTreeFileName, out xmlDocPrjEntityTree);

            
            // create the object to build project with data comingg from the xml content file
            DataBuildProject dataBuildProject  = new DataBuildProject(); 

            // decode entityTree
            if (!transformXmlToProject.PutXmlEntityTreeToProject(xmlDocPrjEntityTree, project, dataBuildProject, out listXmlNodeOnError))
                return _appActivityMgr.AddErrorRetFalse(ProjectRepositXmlDef.ErrorCode.ParamsWrong.ToString(), listXmlNodeOnError);
            // todo:

            return true;
        }

        #endregion
    }
}
